#include "Viewport.h"

#include <algorithm>

#include <Base/Math.h>

#include <EngineInterface/SimulationFacade.h>

#include <EngineInterface/SimulationFacade.h>
#include "WindowController.h"

#include <GLFW/glfw3.h>

void Viewport::setup()
{
    _viewSize = WindowController::get().getStartupWindowSize();
}

float Viewport::getZoomFactor()
{
    return _zoomFactor;
}

void Viewport::setZoomFactor(float zoomFactor)
{
    _zoomFactor = zoomFactor;
}

RealVector2D Viewport::getCenterInWorldPos()
{
    return _centerInWorldPos;
}

void Viewport::setCenterInWorldPos(RealVector2D const& worldCenter)
{
    _centerInWorldPos = worldCenter;
}

IntVector2D Viewport::getViewSize()
{
    return _viewSize;
}

void Viewport::setViewSize(IntVector2D const& viewSize)
{
    _viewSize = viewSize;
}

void Viewport::zoom(IntVector2D const& viewPos, float factor)
{
    auto worldPos = mapViewToWorldPosition({toFloat(viewPos.x), toFloat(viewPos.y)});

    auto newZoomFactor = _zoomFactor * factor;
    newZoomFactor = std::clamp(newZoomFactor, 0.02f, 200.0f);
    _zoomFactor = newZoomFactor;

    moveCenter(worldPos, viewPos);
}

float Viewport::getZoomSensitivity()
{
    return _zoomSensitivity;
}

void Viewport::setZoomSensitivity(float value)
{
    _zoomSensitivity = std::min(10.0f, std::max(1.0f, value));
}

void Viewport::moveCenter(RealVector2D const& startWorldPosition, IntVector2D const& endViewPos)
{
    RealVector2D deltaViewPos{toFloat(endViewPos.x) - toFloat(_viewSize.x) / 2.0f, toFloat(endViewPos.y) - toFloat(_viewSize.y) / 2.0f};
    auto deltaWorldPos = deltaViewPos / _zoomFactor;
    _centerInWorldPos = startWorldPosition - deltaWorldPos;
}

RealVector2D Viewport::mapViewToWorldPosition(RealVector2D const& viewPos)
{
    RealVector2D relCenter{toFloat(_viewSize.x / (2.0 * _zoomFactor)), toFloat(_viewSize.y / (2.0 * _zoomFactor))};
    RealVector2D relWorldPos{viewPos.x / _zoomFactor, viewPos.y / _zoomFactor};
    return _centerInWorldPos - relCenter + relWorldPos;
}

RealVector2D Viewport::mapWorldToViewPosition(RealVector2D worldPos, bool borderlessRendering)
{
    if (borderlessRendering) {
        auto simulationFacade = _SimulationFacade::get();
        auto worldSize = toRealVector2D(simulationFacade->getWorldSize());
        auto offset = _centerInWorldPos - worldSize / 2;
        worldPos.x = Math::modulo(worldPos.x - offset.x, worldSize.x) + offset.x;
        worldPos.y = Math::modulo(worldPos.y - offset.y, worldSize.y) + offset.y;
    }
    return {
        (worldPos.x - _centerInWorldPos.x) * _zoomFactor + toFloat(_viewSize.x) / 2,
        (worldPos.y - _centerInWorldPos.y) * _zoomFactor + toFloat(_viewSize.y) / 2};
}

RealRect Viewport::getVisibleWorldRect()
{
    auto topLeft = mapViewToWorldPosition(RealVector2D{0, 0});
    auto bottomRight = mapViewToWorldPosition(RealVector2D{toFloat(_viewSize.x - 1), toFloat(_viewSize.y - 1)});
    return {topLeft, bottomRight};
}

bool Viewport::isVisible(RealVector2D const& viewPos)
{
    return viewPos.x >= 0 && viewPos.y >= 0 && viewPos.x < _viewSize.x && viewPos.y < _viewSize.y;
}
